/**
* @file SoundManager.h
* @author Max Ocklind
* @version 2010-01-10
**/


#if !defined(SOUNDMANAGER_H)
#define SOUNDMANAGER_H


/* includes */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <vector>
#include <map>

#include "StaticSound.h"
#include "StaticStreamingSound.h"
#include "SFXFilter.h"
#include "Timer.h"


/* defines */

// the number of simulatanious sources is actually hardware dependent
// change it to another value if you want, but modern sound cards can "only" manage about 250 sources
// note that the music always uses one source, so there will be NUM_SOURCES - 1 sources for sounds
#define NUM_SOURCES			   21

#define SOUND_CULLED		   NULL
#define NO_FREE_SOURCES		   NULL
#define MUSIC				   0 // dedicated source index for the music

// some default priorities
#define NO_PRIO				   -1
#define MIN_PRIO			   0
#define LOW_PRIO			   3
#define	MEDIUM_PRIO			   6
#define HIGH_PRIO			   9
#define MAX_PRIO			   INT_MAX


/* classes */

class SoundManager : public Singleton<SoundManager>
{
private:
	/**
	* A pointer to the Open AL error checking class.
	*/
	ALErrorChecker* errCheck;
	
	/**
	* The vector of buffers that holds the sound data.
	*/
	std::vector<SoundBuffer> mSoundBuffers;

	/**
	* The array of available sound sources (max number of simultanious sounds).
	* Note that the first element is reserved for a music track.
	*/
	SoundSource mSources[NUM_SOURCES];

	/**
	* The array of pointers to available sound slots.
	* Note that the first element is reserved for a music track.
	*/
	Sound* mSounds[NUM_SOURCES];

	/**
	* Keeps track of if sounds should be culled depending on distance to the listener.
	*/
	bool mDistanceCulling;

	/**
	* The pointer to the map containing the different types of sounds (strings) and their
	* respective culling distances.
	*/
	std::map<const char*, float>* mCullingDistances;

	/**
	* The pointer to the enabled master sound effect filter, is NULL if disabled.
	*/
	SFXFilter* mMasterSFXFilter;

	/**
	* The timer associated with the enabled master sound effect filter.
	*/
	Timer mMasterSFXFilterTimer;


	Sound* mPlayIfPossible(SoundBuffer buffer, const char* filePath, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, ALboolean loop, ALfloat pitch, ALfloat gain, ALfloat rolloffFactor, SoundType soundType, SFXFilter* filter, unsigned long timeLimit);
	bool   mCheckCullSound(Sound* sound, ALfloat* position, const char* cullType, const char* when);
	
	bool   mIsStatic(SoundType soundType);
	bool   mIsStreaming(SoundType soundType);
	
	void   mResetSoundManagerProperties();

public:
	/**
	* Destructor that deletes all buffers and sources, and shuts down OpenAL.
	*/
	~SoundManager()
	{
		// delete buffers and streaming sounds
		deleteBuffers();

		// delete sources
		alDeleteSources(NUM_SOURCES, mSources);
		errCheck->checkALError("SoundManager", "delete sources");

		// delete Sound objects
		for (int i = 0; i < NUM_SOURCES; i++)
			delete mSounds[i];

		/* shutdown OpenAL */
		ALCcontext* context = alcGetCurrentContext();
		errCheck->checkALCError("SoundManager", "get context");

		// disable context
		alcMakeContextCurrent(NULL);
		errCheck->checkALCError("SoundManager", "set context NULL");

		// destroy context
		alcDestroyContext(context);
		errCheck->checkALCError("SoundManager", "destroy context");
		
		// close device
		alcCloseDevice(alcGetContextsDevice(context));
		errCheck->checkALCError("SoundManager", "close device");

		// exit ALUT
		alutExit();
		errCheck->checkALUTError("SoundManager", "exit");
	} /* ~SoundManager() */


	/* functions */

	void init(ALfloat metersPerUnit);
	void init(ALfloat metersPerUnit, const ALCchar* deviceName);

	void disableDistanceCulling();
	void enableDistanceCulling(std::map<const char*, float>* cullMap);
	bool isDistanceCulling();

	void enableDopplerEffect();
	void disableDopplerEffect();
	bool isDopplerEffect();
	void setDopplerFactor(ALfloat factor);
	void setDopplerFactor(ALfloat factor, ALfloat speedOfSound);
	ALfloat getDopplerFactor();

	void setMasterVolume(ALfloat gain);
	ALfloat getMasterVolume();
	void setMusicVolume(ALfloat gain);
	ALfloat getMusicVolume();
	
	void setListenerProperties(ALfloat* position, ALfloat* velocity, ALfloat* orientation);
	float distanceToListener(Sound* sound, ALfloat* position, const char* when);

	Sound* loadMusic(const char* filePath);
	Sound* loadMusic(const char* filePath, bool loop);
	void playMusic();
	void playMusic(SFXFilter* filter, unsigned long timeLimit);
	void pauseMusic();
	void resumeMusic();
	void stopMusic();

	SoundBuffer loadSound(const char* filePath);
	Sound*		playSound(SoundBuffer buffer, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, bool isStatic);
	Sound*		playSound(SoundBuffer buffer, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, bool isStatic, SFXFilter* filter, unsigned long timeLimit);
	Sound*		playSound(SoundBuffer buffer, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, ALboolean loop, ALfloat pitch, ALfloat gain, ALfloat rolloffFactor, bool isStatic, SFXFilter* filter, unsigned long timeLimit);
	Sound*		playStreaming(const char* filePath, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, bool isStatic);
	Sound*		playStreaming(const char* filePath, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, bool isStatic, SFXFilter* filter, unsigned long timeLimit);
	Sound*		playStreaming(const char* filePath, int priority, const char* cullType, ALfloat* position, ALfloat* velocity, ALboolean loop, ALfloat pitch, ALfloat gain, ALfloat rolloffFactor, bool isStatic, SFXFilter* filter, unsigned long timeLimit);
	
	void update();

	void pauseSounds();
	void resumeSounds();
	void stopSounds();

	void pauseAll();
	void resumeAll();
	void stopAll();

	bool cullSounds();
	bool cullSounds(int priority);
	bool cullSounds(float radius);
	bool cullSounds(int priority, float radius);

	void deleteBuffers();

	void enableMasterSFXFilter(SFXFilter* filter, unsigned long timeLimit);
	void disableMasterSFXFilter();
	void enableMusicSFXFilter(SFXFilter* filter, unsigned long timeLimit);
	void disableMusicSFXFilter();
	void enableSoundsSFXFilter(SFXFilter* filter, unsigned long timeLimit);
	void disableSoundsSFXFilter();
}; /* SoundManager */

#endif /* SOUNDMANAGER_H */